package com.lgc.eventsounds;

import java.util.Iterator;

import javax.swing.Icon;
import javax.swing.JComponent;

import org.jdom.Element;
import com.intellij.debugger.DebuggerManager;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebugProcessListener;
import com.intellij.debugger.engine.SuspendContext;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.configurations.RemoteConnection;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunContentListener;
import com.intellij.openapi.compiler.CompilationStatusListener;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;

public class EventSoundsProjectComponent implements ProjectComponent, CompilationStatusListener, DebugProcessListener, RunContentListener, ProcessListener, Filter, Configurable, JDOMExternalizable {
  public static final String DEFAULT_BREAKPOINT_SOUND = "/com/lgc/eventsounds/sounds/breakpoint.wav";
  public static final String DEFAULT_BUILD_ERROR_SOUND = "/com/lgc/eventsounds/sounds/buildError.wav";
  public static final String DEFAULT_BUILD_SUCCESS_SOUND = "/com/lgc/eventsounds/sounds/buildSuccess.wav";
  public static final String DEFAULT_BUILD_WARNING_SOUND = "/com/lgc/eventsounds/sounds/buildWarning.wav";
  public static final String DEFAULT_EXCEPTION_SOUND = "/com/lgc/eventsounds/sounds/exception.wav";
  public static final String DEFAULT_FIND_FAILED_SOUND = "/com/lgc/eventsounds/sounds/findFailed.wav";
  public static final String DEFAULT_FIND_SUCCESS_SOUND = "/com/lgc/eventsounds/sounds/findSuccess.wav";
  public static final String DEFAULT_PROCESS_STOPPED_SOUND = "/com/lgc/eventsounds/sounds/processStopped.wav";

  protected Project _project = null;
  protected EventSoundsConfiguration _form = null;
  public boolean _breakpointSoundEnabled = true;
  public boolean _buildErrorSoundEnabled = true;
  public boolean _buildSuccessSoundEnabled = true;
  public boolean _buildWarningSoundEnabled = true;
  public boolean _exceptionSoundEnabled = true;
  public boolean _findFailedSoundEnabled = true;
  public boolean _findSuccessSoundEnabled = true;
  public boolean _processStoppedSoundEnabled = true;
  public String _breakpointSound = DEFAULT_BREAKPOINT_SOUND;
  public String _buildErrorSound = DEFAULT_BUILD_ERROR_SOUND;
  public String _buildSuccessSound = DEFAULT_BUILD_SUCCESS_SOUND;
  public String _buildWarningSound = DEFAULT_BUILD_WARNING_SOUND;
  public String _exceptionSound = DEFAULT_EXCEPTION_SOUND;
  public String _findFailedSound = DEFAULT_FIND_FAILED_SOUND;
  public String _findSuccessSound = DEFAULT_FIND_SUCCESS_SOUND;
  public String _processStoppedSound = DEFAULT_PROCESS_STOPPED_SOUND;

  public EventSoundsProjectComponent(Project project)
  {
    _project = project;
  }

  public void initComponent()
  {
    CompilerManager.getInstance(_project).addCompilationStatusListener(this);
    ExecutionManager.getInstance(_project).getContentManager().addRunContentListener(this);
  }

  public void disposeComponent()
  {
    CompilerManager.getInstance(_project).removeCompilationStatusListener(this);
    ExecutionManager.getInstance(_project).getContentManager().removeRunContentListener(this);
  }

  public boolean is_breakpointSoundEnabled()
  {
    return(_breakpointSoundEnabled);
  }

  public void set_breakpointSoundEnabled(boolean breakpointSoundEnabled)
  {
    _breakpointSoundEnabled = breakpointSoundEnabled;
  }

  public boolean is_buildErrorSoundEnabled()
  {
    return(_buildErrorSoundEnabled);
  }

  public void set_buildErrorSoundEnabled(boolean buildErrorSoundEnabled)
  {
    _buildErrorSoundEnabled = buildErrorSoundEnabled;
  }

  public boolean is_buildSuccessSoundEnabled()
  {
    return(_buildSuccessSoundEnabled);
  }

  public void set_buildSuccessSoundEnabled(boolean buildSuccessSoundEnabled)
  {
    _buildSuccessSoundEnabled = buildSuccessSoundEnabled;
  }

  public boolean is_buildWarningSoundEnabled()
  {
    return(_buildWarningSoundEnabled);
  }

  public void set_buildWarningSoundEnabled(boolean buildWarningSoundEnabled)
  {
    _buildWarningSoundEnabled = buildWarningSoundEnabled;
  }

  public boolean is_exceptionSoundEnabled()
  {
    return(_exceptionSoundEnabled);
  }

  public void set_exceptionSoundEnabled(boolean exceptionSoundEnabled)
  {
    _exceptionSoundEnabled = exceptionSoundEnabled;
  }

  public boolean is_findFailedSoundEnabled()
  {
    return(_findFailedSoundEnabled);
  }

  public void set_findFailedSoundEnabled(boolean findFailedSoundEnabled)
  {
    _findFailedSoundEnabled = findFailedSoundEnabled;
  }

  public boolean is_findSuccessSoundEnabled()
  {
    return(_findSuccessSoundEnabled);
  }

  public void set_findSuccessSoundEnabled(boolean findSuccessSoundEnabled)
  {
    _findSuccessSoundEnabled = findSuccessSoundEnabled;
  }

  public boolean is_processStoppedSoundEnabled()
  {
    return(_processStoppedSoundEnabled);
  }

  public void set_processStoppedSoundEnabled(boolean processStoppedSoundEnabled)
  {
    _processStoppedSoundEnabled = processStoppedSoundEnabled;
  }

  public String get_breakpointSound()
  {
    return(_breakpointSound);
  }

  public void set_breakpointSound(String breakpointSound)
  {
    _breakpointSound = breakpointSound;
  }

  public String get_buildErrorSound()
  {
    return(_buildErrorSound);
  }

  public void set_buildErrorSound(String buildErrorSound)
  {
    _buildErrorSound = buildErrorSound;
  }

  public String get_buildSuccessSound()
  {
    return(_buildSuccessSound);
  }

  public void set_buildSuccessSound(String buildSuccessSound)
  {
    _buildSuccessSound = buildSuccessSound;
  }

  public String get_buildWarningSound()
  {
    return(_buildWarningSound);
  }

  public void set_buildWarningSound(String buildWarningSound)
  {
    _buildWarningSound = buildWarningSound;
  }

  public String get_exceptionSound()
  {
    return(_exceptionSound);
  }

  public void set_exceptionSound(String exceptionSound)
  {
    _exceptionSound = exceptionSound;
  }

  public String get_findFailedSound()
  {
    return(_findFailedSound);
  }

  public void set_findFailedSound(String findFailedSound)
  {
    _findFailedSound = findFailedSound;
  }

  public String get_findSuccessSound()
  {
    return(_findSuccessSound);
  }

  public void set_findSuccessSound(String findSuccessSound)
  {
    _findSuccessSound = findSuccessSound;
  }

  public String get_processStoppedSound()
  {
    return(_processStoppedSound);
  }

  public void set_processStoppedSound(String processStoppedSound)
  {
    _processStoppedSound = processStoppedSound;
  }

  public String getComponentName()
  {
    return("EventSoundsProjectComponent");
  }

  public void projectOpened()
  {
  }

  public void projectClosed()
  {
  }

  //CompilationStatusListener interface implementation
  public void compilationFinished(boolean aborted,int errors,int warnings,final CompileContext compileContext)
  {
    if(errors > 0)
      SoundPlayer.playSoundFile(_buildErrorSound,_buildErrorSoundEnabled);
    else if(warnings > 0)
      SoundPlayer.playSoundFile(_buildWarningSound,_buildWarningSoundEnabled);
    else
      SoundPlayer.playSoundFile(_buildSuccessSound,_buildSuccessSoundEnabled);
  }

  //DebugProcessListener interface implementation
  public void connectorIsReady()
  {
  }

  public void paused(SuspendContext suspendContext)
  {
    //No way to know if this is because developer is stepping through the code or because of a breakpoint
    SoundPlayer.playSoundFile(_breakpointSound,false);
  }

  public void resumed(SuspendContext suspendContext)
  {
  }

  public void processDetached(DebugProcess process, boolean closedByUser)
  {
  }

  public void processAttached(DebugProcess process)
  {
  }

  public void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection)
  {
  }

  //RunContentListener interface implementation
  public void contentSelected(RunContentDescriptor descriptor)
  {
    if(descriptor == null)
      return;
    descriptor.getProcessHandler().addProcessListener(this);
    ExecutionConsole ec = descriptor.getExecutionConsole();
    if(ec instanceof ConsoleView) {
      ConsoleView cv = (ConsoleView)ec;
      cv.addMessageFilter(this);
    }
    DebuggerManager.getInstance(_project).addDebugProcessListener(descriptor.getProcessHandler(),this);
  }

  public void contentRemoved(RunContentDescriptor descriptor)
  {
    if(descriptor == null)
      return;
    descriptor.getProcessHandler().addProcessListener(this);
    ExecutionConsole ec = descriptor.getExecutionConsole();
    if(ec instanceof ConsoleView) {
      ConsoleView cv = (ConsoleView)ec;
      cv.addMessageFilter(this);
    }
    DebuggerManager.getInstance(_project).addDebugProcessListener(descriptor.getProcessHandler(),this);
  }

  //ProcessListener interface implementation
  public void startNotified(ProcessEvent event)
  {
  }

  public void processTerminated(ProcessEvent event)
  {
    SoundPlayer.playSoundFile(_processStoppedSound,_processStoppedSoundEnabled);
    event.getProcessHandler().removeProcessListener(this);
    DebuggerManager.getInstance(_project).removeDebugProcessListener(event.getProcessHandler(),this);
  }

  public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed)
  {
  }

  public void onTextAvailable(ProcessEvent event, Key outputType)
  {
  }

  //Filter interface implementation
  public Result applyFilter(String line, int entireLength)
  {
    if(line.contains("Exception"))
      SoundPlayer.playSoundFile(_exceptionSound,_exceptionSoundEnabled);
    return(null);
  }
  
  //Configurable interface implementation
  public String getDisplayName()
  {
    return("Event Sounds");
  }

  public Icon getIcon()
  {
    return null;
  }

  public String getHelpTopic()
  {
    return null;
  }

  public boolean isModified()
  {
    if(_form == null)
      return(false);
    return(_form.isModified(this));
  }

  public void apply() throws ConfigurationException
  {
    if(_form == null)
      return;
    _form.getData(this);
  }

  public void reset()
  {
    if(_form == null)
      return;
    _form.setData(this);
  }

  public void disposeUIResources()
  {
    _form = null;
  }

  public JComponent createComponent()
  {
    if(_form == null)
      _form = new EventSoundsConfiguration();
    return(_form.get_mainPanel());
  }

  //JDOMExternalizable interface implementation
  public void readExternal(Element element) throws InvalidDataException
  {
    DefaultJDOMExternalizer.readExternal(this, element);
  }

  public void writeExternal(Element element) throws WriteExternalException
  {
    DefaultJDOMExternalizer.writeExternal(this, element);
  }
}
